﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ControlLibrary.StyledTextBox
{
    public class Utilities
    {
        protected internal readonly int CFE_AUTOBACKCOLOR = 0x4000000;
        protected internal readonly int CFM_BACKCOLOR = 0x4000000;
        protected internal readonly int CFM_COLOR = 0x40000000;
        protected internal readonly int EM_FORMATRANGE = 0x439;
        protected internal readonly int EM_GETCHARFORMAT = 0x43a;
        protected internal readonly int EM_GETFIRSTVISIBLELINE = 0xce;
        protected internal readonly int EM_GETLINECOUNT = 0xba;
        protected internal readonly int EM_GETSEL = 0xb0;
        protected internal readonly int EM_LINEFROMCHAR = 0xc9;
        protected internal readonly int EM_LINEINDEX = 0xbb;
        protected internal readonly int EM_LINELENGTH = 0xc1;
        protected internal readonly int EM_MOUSESELECT = 0xff;
        protected internal readonly int EM_POSFROMCHAR = 0xd6;
        protected internal readonly int EM_SETCHARFORMAT = 0x444;
        protected internal readonly int EM_SETEVENTMASK = 0x431;
        protected internal readonly int EN_UPDATE = 0x400;
        protected internal readonly int MK_LBUTTON = 1;
        private readonly Control MasterControl;
        private readonly Delegate MasterDelegate;
        protected internal readonly int PRF_CLIENT = 4;
        protected internal readonly int PRF_ERASEBKGND = 8;
        protected internal readonly int PRF_NONCLIENT = 2;
        protected internal readonly int SB_LINEDOWN = 1;
        protected internal readonly int SB_LINEUP;
        protected internal readonly int SCF_SELECTION = 1;
        protected internal readonly int WM_HSCROLL = 0x114;
        protected internal readonly int WM_KEYDOWN = 0x100;
        protected internal readonly int WM_LBUTTONDBLCLK = 0x203;
        protected internal readonly int WM_LBUTTONDOWN = 0x201;
        protected internal readonly int WM_LBUTTONUP = 0x202;
        protected internal readonly int WM_MOUSEACTIVATE = 0x21;
        protected internal readonly int WM_MOUSELEAVE = 0x2a3;
        protected internal readonly int WM_MOUSEMOVE = 0x200;
        protected internal readonly int WM_MOUSEWHEEL = 0x20a;
        protected internal readonly int WM_PRINT = 0x317;
        protected internal readonly int WM_RBUTTONDOWN = 0x204;
        protected internal readonly int WM_SETREDRAW = 11;
        protected internal readonly int WM_VSCROLL = 0x115;
        private ColorMap[] CMap;
        private ImageAttributes IAttribs;

        protected internal Utilities(Delegate DMaster, Control Master)
        {
            MasterDelegate = DMaster;
            MasterControl = Master;
        }

        protected internal void FormatRange(Graphics g, int startChar, int endChar)
        {
            STRUCT_CHARRANGE struct_charrange;
            STRUCT_RECT struct_rect;
            STRUCT_RECT struct_rect2;
            STRUCT_FORMATRANGE struct_formatrange;
            struct_charrange.cpMin = startChar;
            struct_charrange.cpMax = endChar;
            struct_rect.top = 0;
            struct_rect.bottom = ToTwips((MasterControl.ClientSize.Height + 40));
            struct_rect.left = 0;
            if ((MasterControl.Size.Width - MasterControl.ClientSize.Width) == 20)
            {
                struct_rect.right = ToTwips((MasterControl.ClientSize.Width + ((MasterControl.ClientSize.Width)/80f)) + 4f);
            }
            else
            {
                struct_rect.right = ToTwips((MasterControl.ClientSize.Width + ((MasterControl.ClientSize.Width)/100f)) + 5f);
            }
            struct_rect2.top = 0;
            struct_rect2.bottom = ToTwips(MasterControl.Size.Height);
            struct_rect2.left = 0;
            struct_rect2.right = ToTwips(MasterControl.Size.Width);
            IntPtr hdc = g.GetHdc();
            struct_formatrange.chrg = struct_charrange;
            struct_formatrange.hdc = hdc;
            struct_formatrange.hdcTarget = hdc;
            struct_formatrange.rc = struct_rect;
            struct_formatrange.rcPage = struct_rect2;
            IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(struct_formatrange));
            Marshal.StructureToPtr(struct_formatrange, ptr, false);
            SendMessageToMaster(EM_FORMATRANGE, (IntPtr) 1, ptr, 1);
            SendMessageToMaster(EM_FORMATRANGE, IntPtr.Zero, IntPtr.Zero, -1);
            Marshal.FreeCoTaskMem(ptr);
            g.ReleaseHdc(hdc);
        }

        protected internal void GetRTHighlight(ref Color back, ref Color fore, Color AlphaBackColor)
        {
            var structure = new CHARFORMAT2();
            structure.cbSize = Marshal.SizeOf(structure);
            IntPtr ptr = Marshal.AllocCoTaskMem(structure.cbSize);
            Marshal.StructureToPtr(structure, ptr, false);
            ptr = (IntPtr) SendMessageToMaster(EM_GETCHARFORMAT, (IntPtr) SCF_SELECTION, ptr, 3);
            structure = (CHARFORMAT2) Marshal.PtrToStructure(ptr, typeof (CHARFORMAT2));
            back = (structure.crBackColor == 0) ? AlphaBackColor : ColorTranslator.FromWin32(structure.crBackColor);
            fore = ColorTranslator.FromWin32(structure.crTextColor);
            Marshal.FreeCoTaskMem(ptr);
        }

        protected internal Bitmap MapColors(Color Old, Color New, Bitmap Bmp, bool Dspse)
        {
            var image = new Bitmap(Bmp.Width, Bmp.Height);
            Graphics graphics = Graphics.FromImage(image);
            CMap = new[] {new ColorMap()};
            CMap[0].OldColor = Old;
            CMap[0].NewColor = New;
            IAttribs = new ImageAttributes();
            IAttribs.SetRemapTable(CMap, ColorAdjustType.Bitmap);
            graphics.DrawImage(Bmp, new Rectangle(new Point(0, 0), new Size(Bmp.Width, Bmp.Height)), 0, 0, Bmp.Width, Bmp.Height, GraphicsUnit.Pixel, IAttribs);
            graphics.Dispose();
            if (Dspse)
            {
                Bmp.Dispose();
            }
            return image;
        }

        protected internal Bitmap MapRichTextColors(Color Old, Color New, Bitmap Bmp, bool Dspse)
        {
            var image = new Bitmap(MasterControl.ClientSize.Width, MasterControl.ClientSize.Height);
            Graphics graphics = Graphics.FromImage(image);
            graphics.Clear(New);
            CMap = new[] {new ColorMap()};
            CMap[0].OldColor = Old;
            CMap[0].NewColor = Color.Transparent;
            IAttribs = new ImageAttributes();
            IAttribs.SetRemapTable(CMap, ColorAdjustType.Bitmap);
            graphics.DrawImage(Bmp, new Rectangle(new Point(0, 0), new Size(Bmp.Width, Bmp.Height)), 0, 0, Bmp.Width, Bmp.Height, GraphicsUnit.Pixel, IAttribs);
            graphics.Dispose();
            if (Dspse)
            {
                Bmp.Dispose();
            }
            return image;
        }

        protected internal object SendMessageToMaster(int Msg, IntPtr WParams, IntPtr LParams, int ReturnInstance)
        {
            Message message = Message.Create(MasterControl.Handle, Msg, WParams, LParams);
            var args = new object[] {message};
            MasterDelegate.DynamicInvoke(args);
            switch (ReturnInstance)
            {
                case 0:
                    return args[0];

                case 1:
                    {
                        var message2 = (Message) args[0];
                        return message2.Result;
                    }
                case 2:
                    {
                        var message3 = (Message) args[0];
                        return message3.WParam;
                    }
                case 3:
                    {
                        var message4 = (Message) args[0];
                        return message4.LParam;
                    }
            }
            return -1;
        }

        protected internal void SetRTHighlight(Color back, Color fore)
        {
            var structure = new CHARFORMAT2();
            structure.dwMask = CFM_BACKCOLOR | CFM_COLOR;
            structure.cbSize = Marshal.SizeOf(structure);
            structure.crBackColor = ColorTranslator.ToWin32(back);
            structure.crTextColor = ColorTranslator.ToWin32(fore);
            IntPtr ptr = Marshal.AllocCoTaskMem(structure.cbSize);
            Marshal.StructureToPtr(structure, ptr, false);
            SendMessageToMaster(EM_SETCHARFORMAT, (IntPtr) SCF_SELECTION, ptr, 1);
            Marshal.FreeCoTaskMem(ptr);
        }

        private int ToTwips(float amt)
        {
            return (int) (amt*14.4f);
        }

        #region Nested type: CHARFORMAT2

        [StructLayout(LayoutKind.Sequential)]
        private struct CHARFORMAT2
        {
            public int cbSize;
            public int dwMask;
            public readonly int dwEffects;
            public readonly int yHeight;
            public readonly int yOffset;
            public int crTextColor;
            public readonly byte bCharSet;
            public readonly byte bPitchAndFamily;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] public readonly string szFaceName;
            public readonly ushort wWeight;
            public readonly ushort sSpacing;
            public int crBackColor;
            public readonly int lcid;
            public readonly int dwReserved;
            public readonly ushort sStyle;
            public readonly ushort wKerning;
            public readonly byte bUnderlineType;
            public readonly byte bAnimation;
            public readonly byte bRevAuthor;
            public readonly byte bReserved1;
        }

        #endregion

        #region Nested type: STRUCT_CHARRANGE

        [StructLayout(LayoutKind.Sequential)]
        private struct STRUCT_CHARRANGE
        {
            public int cpMin;
            public int cpMax;
        }

        #endregion

        #region Nested type: STRUCT_FORMATRANGE

        [StructLayout(LayoutKind.Sequential)]
        private struct STRUCT_FORMATRANGE
        {
            public IntPtr hdc;
            public IntPtr hdcTarget;
            public STRUCT_RECT rc;
            public STRUCT_RECT rcPage;
            public STRUCT_CHARRANGE chrg;
        }

        #endregion

        #region Nested type: STRUCT_RECT

        [StructLayout(LayoutKind.Sequential)]
        private struct STRUCT_RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        #endregion
    }
}